namespace Angelus.Oracle.Entity.OracleClient.OracleGen
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using System.Data.Common.CommandTrees;
    using System.Data.Metadata.Edm;
    using System.IO;
    using global::Oracle.DataAccess.Client;

    /// <summary>
    /// Represents a set of methods for creating correct command definition objects and accessing provider
    /// manifest information.
    /// </summary>
    /// <remarks>
    /// The DbProviderServices class is the starting point for accessing the ORACLE generation layer to
    /// convert CommandTrees into DbCommands. This class also provides additional data storage information,
    /// such as the provider manifest, which describes stroage-specific type mapping and functions, and
    /// the storage-specific mapping files to generate queries for table and column information.
    /// </remarks>
    public sealed class OracleEntityProviderServices : DbProviderServices
    {
        #region Fields

        /// <summary>
        /// 
        /// </summary>
        internal static readonly OracleEntityProviderServices Instance = new OracleEntityProviderServices();

        #endregion

        #region DbProviderServices Overrides

        /// <summary>
        /// 
        /// </summary>
        /// <param name="manifestToken"></param>
        /// <returns></returns>
        protected override DbProviderManifest GetDbProviderManifest(string manifestToken)
        {
            if (string.IsNullOrEmpty(manifestToken))
            {
                throw Error.ArgumentNull("manifestToken");
            }

            return new OracleProviderManifest(manifestToken);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connection"></param>
        /// <returns></returns>
        protected override string GetDbProviderManifestToken(DbConnection connection)
        {
            var requirements = new OracleServerRequirements(connection);
            return OracleServerRequirements.GetOracleServerVersion(requirements.OracleServerVersion);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="providerManifest"></param>
        /// <param name="commandTree"></param>
        /// <returns></returns>
        protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest,
                                                                         DbCommandTree commandTree)
        {
            if (null == providerManifest)
            {
                throw Error.ArgumentNull("providerManifest");
            }

            if (!(providerManifest is OracleProviderManifest))
            {
                throw Error.ProviderManifestTokenNotFound();
            }

            var command = this.CreateCommand(providerManifest, commandTree);
            return CreateCommandDefinition(command);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="providerManifest"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        private DbCommand CreateCommand(DbProviderManifest providerManifest, DbCommandTree tree)
        {
            if (tree == null)
            {
                throw new ArgumentNullException("tree");
            }

            var command = new DbOracleCommand {CommandType = CommandType.Text};
            List<string> list = null;
            List<Type> list2 = null;
            if (tree is DbQueryCommandTree)
            {
                foreach (var pair in tree.Parameters)
                {
                    var parameter = new OracleParameter();
                    command.Parameters.Add(parameter);
                }

                var queryTree = tree as DbQueryCommandTree;
                var projectExpression = queryTree.Query as DbProjectExpression;
                if (projectExpression != null)
                {
                    EdmType resultsType = projectExpression.Projection.ResultType.EdmType;

                    var resultsAsStructuralType = resultsType as StructuralType;
                    if (resultsAsStructuralType != null)
                    {
                        command.ExpectedColumnTypes = new PrimitiveType[resultsAsStructuralType.Members.Count];

                        for (var ordinal = 0; ordinal < resultsAsStructuralType.Members.Count; ordinal++)
                        {
                            EdmMember member = resultsAsStructuralType.Members[ordinal];
                            var primitiveType = member.TypeUsage.EdmType as PrimitiveType;
                            command.ExpectedColumnTypes[ordinal] = primitiveType;
                        }
                    }
                }

                var generator = new OracleQueryGenerator();
                command.CommandText = generator.GenerateQuery((DbQueryCommandTree) tree);

                Console.WriteLine(command.CommandText);
                
                using (TextWriter writer = new StreamWriter(@"c:\EFQueries.txt", true))
                {
                    writer.Write(command.CommandText);
                    writer.Flush();
                    writer.Close();
                }

                command.CommandType = CommandType.Text;
            }

            return command;
        }

        #endregion
    }
}